Skip to content

feat: Add uv support for python packaging#723

Merged
antonbabenko merged 9 commits intoterraform-aws-modules:masterfrom
rivhar:feat/add-uv-support
Jan 26, 2026
Merged

feat: Add uv support for python packaging#723
antonbabenko merged 9 commits intoterraform-aws-modules:masterfrom
rivhar:feat/add-uv-support

Conversation

@rivhar
Copy link
Contributor

@rivhar rivhar commented Jan 5, 2026

Description

This PR adds support for the uv package manager for Python Lambda functions. It introduces a new build system autodetection logic in package.py and provides updated build environments via Docker.

Key changes include:
Autodetection: Added logic to recognize uv as a build system when a uv.lock or specific pyproject.toml configuration is present.
Docker Support: Updated the Lambda build Dockerfile to include uv (pinned to version 0.9.21) to ensure stable, containerized builds.
Layer Compatibility: Integrated uv export with the existing prefix_in_zip logic to ensure dependencies are correctly placed in the python/ folder for Lambda Layers.
Examples: Created new fixtures and examples in examples/fixtures/python-app-uv and examples/build-package to demonstrate local and Docker-based UV builds.

Motivation and Context

uv is an extremely fast Python package manager that is gaining significant traction in the community. Adding native support allows users to benefit from faster build times and reliable dependency resolution (via uv.lock) without leaving the terraform-aws-lambda ecosystem.
Closes #673

Breaking Changes

None. This change is purely additive. Existing Poetry and Pip-based workflows remain untouched. The autodetection logic is designed to fall back to existing methods if uv specific files are not found.

How Has This Been Tested?

[x] I have updated at least one of the examples/* to demonstrate and validate my change(s)
[x] I have tested and validated these changes using one or more of the provided examples/* projects
[x] I have executed pre-commit run -a on my pull request

Detailed testing performed:

Unit Testing: Ran pytest tests/test_package_toml.py. All tests for the new UV build system detection passed.
Integration (Local): Deployed module.package_dir_uv_no_docker using a local uv binary; verified the resulting ZIP contained all dependencies.
Integration (Docker): Deployed module.package_dir_uv using the updated Dockerfile; verified uv export and pip install executed correctly inside the container.
Layer Verification: Inspected the ZIP artifact for module.lambda_layer_uv using zipinfo. Verified all dependencies are correctly prefixed with python/ for Lambda runtime compatibility.

Additional Updates in this PR
Automatic uv.lock generation from pyproject.toml
Introduced a new behavior: if uv.lock is missing but pyproject.toml exists, the build automatically runs uv lock to generate it.
This ensures builds succeed without a pre-existing lock file.
Added a dedicated fixture python-app-uv-no-lock in examples/fixtures/ to validate this behavior.
Tested all the modules including the new one related to missing uv.lock

@rivhar rivhar force-pushed the feat/add-uv-support branch from f44447c to 6a499cf Compare January 5, 2026 19:53
@rivhar rivhar changed the title feat: add uv support for python packaging feat: Add uv support for python packaging Jan 5, 2026
@rivhar rivhar force-pushed the feat/add-uv-support branch from fdef682 to 88d8221 Compare January 9, 2026 18:57
@joshshand-coop
Copy link

@rivhar - thanks for putting the work into this. Quick question - you mention in your description: "Added logic to recognize uv as a build system when a uv.lock or specific pyproject.toml configuration is present."

Perhaps I am mistaken, but looking at the changes to package.py it would seem that you require a uv.lock file to be present to execute the remaining code. However, wouldn't you want the behavior to be that it attempts to generate uv.lock based on pyproject.toml on first run IF there is no current uv.lock - similar to how terraform lock file operates?

I could be missing something obvious :)

@rivhar
Copy link
Contributor Author

rivhar commented Jan 18, 2026

@joshshand-coop - You're right to point it out. The initial logic was a bit too strict. I've updated package.py to support that exact 'auto-lock' behavior: if uv.lock is missing but a pyproject.toml is found, the script now automatically triggers uv lock to generate it before proceeding with the build. Pushing the changes now—thanks for the feedback:)

Please find "Additional Updates in this PR" section in PR description at the end in order to get idea of the changes made.

@rivhar rivhar force-pushed the feat/add-uv-support branch from 688b4f4 to 04932fd Compare January 18, 2026 16:14
Copy link
Member

@antonbabenko antonbabenko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks pretty good, and it works 95% of the times :) Please address the remaining comments before we can merge it.

package.py Outdated

if not os.path.exists(uv_lock_file) and os.path.exists(pyproject_file):
try:
check_call([uv_exec, "lock"], cwd=project_path)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would write uv.lock into the source directory, which can be read-only.

We either need to require uv.lock to exist, or generate in temp directory (if it doesn't exist).

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest that it attempts to write to the source directory (if uv.lock doesn't exist) with a fallback of a temp directory.
Reasoning is that the user may wish to then commit that generated lock file to source control after running uv via this module. If it's in a temp dir it's possible it may not exist post execution.

package.py Outdated
with open(requirements_file, "r") as f:
for line in f:
stripped = line.strip()
if stripped == "-e .":
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line bites me during my own test run (I rely on this command).

Let's make this conditional or document the required workaround (I don't know it myself), and don't skip it every time.

Copy link
Contributor Author

@rivhar rivhar Jan 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@antonbabenko
Thanks for the feedback, Anton:)

I’ve updated the logic to address both points:
Read-only Safety: uv lock now runs inside the temporary directory (temp_dir) instead of the source directory, ensuring it works even if the source is read-only.
Conditional Skip: The -e . stripping is now scoped specifically to Lambda packaging builds (by checking for runtime and artifacts_dir in the query). This preserves your local test runs while keeping Lambda builds safe.

Please let me know if there is something else!

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@antonbabenko Thanks for the feedback, Anton:)

I’ve updated the logic to address both points: Read-only Safety: uv lock now runs inside the temporary directory (temp_dir) instead of the source directory, ensuring it works even if the source is read-only. Conditional Skip: The -e . stripping is now scoped specifically to Lambda packaging builds (by checking for runtime and artifacts_dir in the query). This preserves your local test runs while keeping Lambda builds safe.

Please let me know if there is something else!

@rivhar see my comment above re temp dir if you haven't :-)

Copy link
Contributor Author

@rivhar rivhar Jan 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@joshshand-coop,
The function now generates uv.lock in a temp directory if it doesn’t exist, and then attempts to copy it back to the source directory. Existing uv.lock is used as-is. This should cover both safe temp execution and the option to commit the lock file afterward.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rivhar I think always generating the uv.lock in a temporary directory makes sense to ensure stability of uv execution.
After generation, if the source directory is writeable, you could then copy from temp to source as well?
If it IS read-only - just log an informational message instead.

Copy link
Contributor Author

@rivhar rivhar Jan 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@joshshand-coop
I have updated the function so that uv.lock is always generated in a temporary directory for stable execution. After generation, if the uv lock was generated and source directory is writable, it’s copied back; if it’s read-only, we just log an informational message. This ensures consistent behavior without failing on read-only source paths.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@joshshand-coop I have updated the function so that uv.lock is always generated in a temporary directory for stable execution. After generation, if the uv lock was generated and source directory is writable, it’s copied back; if it’s read-only, we just log an informational message. This ensures consistent behavior without failing on read-only source paths.

Sounds great, nice one

@rivhar rivhar force-pushed the feat/add-uv-support branch from 366edcd to 49f7e43 Compare January 25, 2026 21:04
@rivhar rivhar force-pushed the feat/add-uv-support branch from d7a22ee to 6bfe283 Compare January 25, 2026 21:54
This commit fixes 3 critical issues identified during PR review:

1. CRITICAL: Silent failure in error handling (package.py:1607-1619)
   - Replaced broad 'except Exception' with specific exceptions
   - Changed debug logging to warning level for visibility
   - Added actionable error messages for users
   - Added success logging when lock file is saved

2. CRITICAL: Unprotected file removal (package.py:1621-1631)
   - Wrapped os.remove() calls in try-except blocks
   - Prevents crashes after successful build
   - Follows project's TemporaryCopy pattern

3. Enhanced error handling for lock generation (package.py:1520-1548)
   - Pre-check uv availability before attempting to use it
   - Added specific CalledProcessError handling
   - Improved error messages with exit codes and remediation steps
   - Added success logging

4. Test coverage: Added 8 comprehensive tests
   - Tests for basic functionality with existing lock
   - Tests for auto-lock generation
   - Tests for error scenarios (missing uv, generation failures)
   - Tests for edge cases (read-only directories)
   - Tests for build system detection

Impact:
- Prevents silent failures in production
- Provides clear, actionable error messages
- Improves debuggability with proper logging
- Adds test coverage for critical paths

Related: terraform-aws-modules#723, terraform-aws-modules#673
Copy link
Member

@antonbabenko antonbabenko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot to everyone for the contribution to this module!

@antonbabenko antonbabenko merged commit 8dc36e8 into terraform-aws-modules:master Jan 26, 2026
28 of 30 checks passed
antonbabenko pushed a commit that referenced this pull request Jan 26, 2026
## [8.4.0](v8.3.0...v8.4.0) (2026-01-26)

### Features

* Add uv support for python packaging ([#723](#723)) ([8dc36e8](8dc36e8))
@antonbabenko
Copy link
Member

This PR is included in version 8.4.0 🎉

@github-actions
Copy link

I'm going to lock this pull request because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active issues. If you have found a problem that seems related to this change, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Feb 26, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feat Request: Add support for uv based Python builds

3 participants